﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Noris.Tools.TraceVisualiser.BlockingVisualiser.Components
{
    /// <summary>
    /// Prostor obdélníku. Objekt je rovnocený se strukturou RectangleF, ale má pár rozdílů:
    /// 1) je to class
    /// 2) má svůj bod Center, za který je možno přemisťovat celý objekt (beze změny rozměru)
    /// 3) má svoje body v každém rohu, které je možno měnit
    /// </summary>
    internal class Area
    {
        #region Konstruktory a implicitní konvertory na RectangleF
		public Area()
		{
			this._Left = 0f;
			this._Top = 0f;
			this._Right = 0f;
			this._Bottom = 0f;
		}
        public Area(float x, float y, float width, float height)
        {
            this._Left = x;
            this._Top = y;
            this._Right = x + width - 1f;
            this._Bottom = y + height - 1f;
            CheckSwap();
        }
        public Area(PointF location, SizeF size)
        {
            this._Left = location.X;
            this._Top = location.Y;
            this._Right = location.X + size.Width - 1f;
            this._Bottom = location.Y + size.Height - 1f;
            CheckSwap();
        }
        public Area(RectangleF rectangle)
        {
            this._Left = rectangle.X;
            this._Top = rectangle.Y;
            this._Right = rectangle.Right - 1f;
            this._Bottom = rectangle.Bottom - 1f;
            CheckSwap();
        }
        public Area(PointF point1, PointF point2)
        {
            this._Left = point1.X;
            this._Top = point1.Y;
            this._Right = point2.X;
            this._Bottom = point2.Y;
            CheckSwap();
        }
		/// <summary>
		/// Implicitní konverze Area - RectangleF
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
        public static implicit operator RectangleF(Area value) { return value.Bounds; }
		/// <summary>
		/// Implicitní konverze Area - RectangleF
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
        public static implicit operator Area(RectangleF value) { return new Area(value); }
        #endregion
        #region Jediné čtyři proměnné ukládající data do paměti
        private float _Left;
        private float _Right;
        private float _Top;
        private float _Bottom;
        #endregion
        #region Interní kontrolní metody volané po zadání dat
        private void CheckSwap()
        {
            this.CheckSwapX();
            this.CheckSwapY();
        }
        private void CheckSwapX()
        {
            if (this._Right < this._Left)
            {
                float v = this._Right;
                this._Right = this._Left;
                this._Left = v;
            }
        }
        private void CheckSwapY()
        {
            if (this._Bottom < this._Top)
            {
                float v = this._Bottom;
                this._Bottom = this._Top;
                this._Top = v;
            }
        }
        #endregion
        #region Základní property
        /// <summary>
        /// Left = souřadnice X bodů vlevo.
        /// Změna hodnoty provede změnu hodnoty Right, ponechá beze změn hodnotu Width.
        /// </summary>
        public float Left
        {
            get { return this._Left; }
            set
            {
                float w = this.Width;
                this._Left = value;
                this._Right = value + w;
                this.OnChangeX();
            }
        }
        /// <summary>
        /// Right = souřadnice X bodů vpravo.
        /// Změna hodnoty provede změnu hodnoty Width, ponechá beze změn hodnotu Left.
        /// Pokud se zadá hodnota menší než Left, pak se do Right vloží hodnota Left namísto hodnoty zadané.
        /// </summary>
        public float Right
        {
            get { return this._Right; }
            set
            {
                if (value > this._Left)
                    this._Right = value;
                else
                    this._Right = this._Left;
                this.OnChangeX();
            }
        }
        /// <summary>
        /// Top = souřadnice Y bodů nahoře.
        /// Změna hodnoty provede změnu hodnoty Bottom, ponechá beze změn hodnotu Height.
        /// </summary>
        public float Top
        {
            get { return this._Top; }
            set
            {
                float h = this.Height;
                this._Top = value;
                this._Bottom = value + h;
                this.OnChangeY();
            }
        }
        /// <summary>
        /// Bottom = souřadnice Y bodů dole.
        /// Změna hodnoty provede změnu hodnoty Height, ponechá beze změn hodnotu Top.
        /// Pokud se zadá hodnota menší než Top, pak se do Bottom vloží hodnota Top namísto hodnoty zadané.
        /// </summary>
        public float Bottom
        {
            get { return this._Bottom; }
            set
            {
                if (value > this._Top)
                    this._Bottom = value;
                else
                    this._Bottom = this._Top;
                this.OnChangeY();
            }
        }
        #endregion
        #region Odvozené property Width, Height, CenterX, CenterY
        /// <summary>
        /// Šířka objektu.
        /// Změnou hodnoty se objekt rozšíří / zúží, zůstane zachována pozice Left, změní se Right.
        /// Je možno použít metodu ReSizeWidth, do níž lze zadat relativní bod středu, okolo kterého změna proběhne.
        /// </summary>
        public float Width
        {
            get { return this._Right - this._Left + 1f; }
            set
            {
                this._Right = this._Left + value - 1f; 
                this.CheckSwapX();
                this.OnChangeX();
            }
        }
        /// <summary>
        /// Výška objektu.
        /// Změnou hodnoty se objekt zvýší / sníží, zůstane zachována pozice Left, změní se Right.
        /// Je možno použít metodu ReSizeWidth, do níž lze zadat relativní bod středu, okolo kterého změna proběhne.
        /// </summary>
        public float Height
        {
            get { return this._Bottom - this._Top + 1f; }
            set
            {
                this._Bottom = this._Top + value - 1f;
                this.CheckSwapY();
                this.OnChangeY();
            }
        }
        /// <summary>
        /// Střed v ose X.
        /// Změnou středu se objekt posune na nový střed - nezmění se jeho velikost.
        /// </summary>
        public float CenterX
        {
            get { return this._Left + this.Width / 2f; }
            set
            {
                float shift = value - this.CenterX;
                this._Left += shift;
                this._Right += shift;
                this.OnChangeX();
            }
        }
        /// <summary>
        /// Střed v ose Y.
        /// Změnou středu se objekt posune na nový střed - nezmění se jeho velikost.
        /// </summary>
        public float CenterY
        {
            get { return this.Top + this.Height / 2f; }
            set
            {
                float shift = value - this.CenterY;
                this._Top += shift;
                this._Bottom += shift;
                this.OnChangeY();
            }
        }
        #endregion
        #region Odvozené property typu PointF
        /// <summary>
        /// Bod vlevo nahoře
        /// </summary>
        public PointF PointTopLeft
        {
            get { return new PointF(this._Left, this._Top); }
            set
            {
                this._Left = value.X;
                this._Top = value.Y;
                CheckSwap();
                this.OnChange();
            }
        }
        /// <summary>
        /// Bod vlevo dole
        /// </summary>
        public PointF PointBottomLeft
        {
            get { return new PointF(this._Left, this._Bottom); }
            set
            {
                this._Left = value.X;
                this._Bottom = value.Y;
                CheckSwap();
                this.OnChange();
            }
        }
        /// <summary>
        /// Bod vpravo dole
        /// </summary>
        public PointF PointBottomRight
        {
            get { return new PointF(this._Right, this._Bottom); }
            set
            {
                this._Right = value.X;
                this._Bottom = value.Y;
                CheckSwap();
                this.OnChange();
            }
        }
        /// <summary>
        /// Bod vpravo nahoře
        /// </summary>
        public PointF PointTopRight
        {
            get { return new PointF(this._Right, this._Top); }
            set
            {
                this._Right = value.X;
                this._Top = value.Y;
                CheckSwap();
                this.OnChange();
            }
        }
        /// <summary>
        /// Bod uprostřed
        /// </summary>
        public PointF PointCenter
        {
            get { return new PointF(this.CenterX, this.CenterY); }
            set
            {
                this.CenterX = value.X;
                this.CenterY = value.Y;
            }
        }
        #endregion
        #region Property typu Plocha
        /// <summary>
        /// Maximální prostor tohoto objektu, vyjádřený standardním RectangleF.
        /// Nad rámec tohoto prostoru se nic nevykresluje.
        /// </summary>
        public RectangleF Bounds
        {
            get { return new RectangleF(this.PointTopLeft, this.Size); }
            set
            {
                this._Top = value.Top;
                this._Left = value.Left;
                this._Right = value.Right - 1f;
                this._Bottom = value.Bottom - 1f ;
                this.CheckSwap();
                this.OnChange();
            }
        }
        /// <summary>
        /// Velikost tohoto objektu
        /// </summary>
        public SizeF Size
        {
            get { return new SizeF(this.Width, this.Height); }
            set
            {
                this._Right = this._Left + value.Width - 1f;
                this._Bottom = this._Top + value.Height - 1f;
                this.CheckSwap();
                this.OnChange();
            }
        }
        #endregion
		#region Property pro podporu textu
		/// <summary>
		/// Text vypisovaný v ploše
		/// </summary>
		public string Text { get { return this._Text; } set { this._Text = value; this._TextBoundsReset(); } }
		public Font TextFont { get { return this._TextFont; } set { this._TextFont = value; this._TextBoundsReset(); } }
		public Color TextColor { get { return this._TextColor; } set { this._TextColor = value; } }
		/// <summary>
		/// Umístění textu v rámci this objektu
		/// </summary>
		public System.Drawing.ContentAlignment TextAlignement { get { return this._TextAlignement; } set { this._TextAlignement = value; this._TextBoundsCalculate(); } }
		/// <summary>
		/// Okraje mezi this.Bounds a this.TextBounds
		/// </summary>
		public System.Windows.Forms.Padding TextPadding { get { return this._TextPadding; } set { this._TextPadding = value; this._TextBoundsCalculate(); } }
		/// <summary>
		/// Obsahuje plnou informaci o umístění textu v rámci this.
		/// Nejprve je třeba vložit hodnotu do TextSize, přitom se vypočte aktuální pozice.
		/// Anebo nechat vypočítat TextSize a TextBounds pomocí metody TextCalculate().
		/// </summary>
		public RectangleF TextBounds { get { return this._TextBounds; } }
		/// <summary>
		/// Velikost textu.
		/// Vložení hodnoty určí pozici textu do TextBounds.
		/// </summary>
		public SizeF TextSize { get { return this._TextSize; } set { this._TextSize = value; this._TextBoundsCalculate(); } }
		/// <summary>
		/// Určí velikost textu a jeho pozici:
		/// Vypočte data do this.TextSize a this.TextBounds
		/// </summary>
		/// <param name="graphics"></param>
		/// <param name="font"></param>
		public void TextCalculate(Graphics graphics, Font font)
		{
			this._TextSize = graphics.MeasureString(this.Text, font);
			this._TextBoundsCalculate();
		}
		/// <summary>
		/// Vynuluje pozici textu
		/// </summary>
		private void _TextBoundsReset()
		{
			this._TextSize = SizeF.Empty;
			this._TextBounds = RectangleF.Empty;
		}
		/// <summary>
		/// Na základě velikosti textu, jeho zarovnání a padding a this velikosti určí pozici textu.
		/// </summary>
		private void _TextBoundsCalculate()
		{
			RectangleF innerArea = new RectangleF(this.Bounds.X + this.TextPadding.Top, this.Bounds.Y + this.TextPadding.Left,
				this.Bounds.Width - this.TextPadding.Horizontal, this.Bounds.Height - this.TextPadding.Vertical);
			SizeF textSize = this._TextSize;
			if (textSize.Width > innerArea.Width)
				textSize.Width = innerArea.Width;
			if (textSize.Height > innerArea.Height)
				textSize.Height = innerArea.Height;
			float x = innerArea.Width - textSize.Width;
			float y = innerArea.Height - textSize.Height;
			PointF textLocation = innerArea.Location;
			switch (this._TextAlignement)
			{
				case ContentAlignment.TopLeft:
					break;
				case ContentAlignment.TopCenter:
					textLocation.X += x / 2f;
					break;
				case ContentAlignment.TopRight:
					textLocation.X += x;
					break;
				case ContentAlignment.MiddleLeft:
					textLocation.Y += y / 2f;
					break;
				case ContentAlignment.MiddleCenter:
					textLocation.X += x / 2f;
					textLocation.Y += y / 2f;
					break;
				case ContentAlignment.MiddleRight:
					textLocation.X += x;
					textLocation.Y += y / 2f;
					break;
				case ContentAlignment.BottomLeft:
					textLocation.Y += y;
					break;
				case ContentAlignment.BottomCenter:
					textLocation.X += x / 2f;
					textLocation.Y += y;
					break;
				case ContentAlignment.BottomRight:
					textLocation.X += x;
					textLocation.Y += y;
					break;
			}
			this._TextBounds = new RectangleF(textLocation, textSize);
		}
		private string _Text;
		private Font _TextFont;
		private Color _TextColor;
		private System.Drawing.ContentAlignment _TextAlignement;
		private System.Windows.Forms.Padding _TextPadding;
		private RectangleF _TextBounds;
		private SizeF _TextSize;

		#endregion
        #region Detekce příslušnosti bodu
        /// <summary>
        /// Metoda vrátí infromaci o tom, zda daný bod se nachází uvnitř tohoto objektu (true) nebo mimo něj (false).
        /// Typicky slouží pro detekci pohybu / kliknutí myší na plochu.
        /// Interně pracuje s (virtuální) property this.Path, tím funguje i s tvary definovanými na potomkovi.
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public virtual bool ContainPoint(PointF point)
        {
            GraphicsPath path = this.Path;
            return path.IsVisible(point);
        }
        #endregion
        #region GraphicsPath
        /// <summary>
        /// Vrátí GraphicsPath.
        /// Bázová třída vrací cestu: TopLeft - BottomLeft - BottomRight - TopRight - TopLeft.
        /// </summary>
        public virtual GraphicsPath Path
        {
            get
            {
                GraphicsPath path = new GraphicsPath();
                path.AddLines(new PointF[] { this.PointTopLeft, this.PointBottomLeft, this.PointBottomRight, this.PointTopRight, this.PointTopLeft });
                path.CloseFigure();
                return path;
            }
        }
        #endregion
        #region Public metody pro změny
        /// <summary>
        /// Změna velikosti pomocí koeficientu.
        /// </summary>
        /// <param name="coefficient"></param>
        public void Scale(float coefficient)
        {
            this.ScaleX(coefficient, 0.5f);
            this.ScaleY(coefficient, 0.5f);
        }
        /// <summary>
        /// Změna velikosti pomocí koeficientu.
        /// </summary>
        /// <param name="coefficientX"></param>
        /// <param name="coefficientY"></param>
        /// <param name="center"></param>
        public void Scale(float coefficientX, float coefficientY, PointF center)
        {
            float w = this.Width;
            if (w != 0f)
                this.ScaleX(coefficientX, (center.X - this._Left) / w);

            float h = this.Height;
            if (h != 0f)
                this.ScaleY(coefficientY, (center.Y - this._Top) / h);
        }
        /// <summary>
        /// Změna velikosti pomocí koeficientu.
        /// </summary>
        /// <param name="coefficientX"></param>
        /// <param name="coefficientY"></param>
        /// <param name="center"></param>
        public void Scale(float coefficientX, float coefficientY, float relativeCenterX, float relativeCenterY)
        {
            this.ScaleX(coefficientX, relativeCenterX);
            this.ScaleY(coefficientY, relativeCenterY);
        }
        /// <summary>
        /// Upraví rozměry v ose X daným koeficientem (změna šířky) s daným fixním bodem, okolo kterého změna šířky proběhne
        /// </summary>
        /// <param name="coefficientX">Koeficient změny šířky. 1.0f = šířka se nezmění. 0 = šířka se vynuluje. Záporné číslo: objekt se zrcadlí okolo dané souřadnice.</param>
        /// <param name="relativeCenterX">Relativní pozice bodu, okolo které změna proběhne (tento bod se "nepohne"). 0 = Left, 1 = Right, 0.5f = střed objektu.</param>
        public void ScaleX(float coefficientX, float relativeCenterX)
        {
            float w = this.Width;
            if (w == 0f || coefficientX == 1f) return;
            float c = this.PointXFromRelative(relativeCenterX);
            w = w * coefficientX;
            this._Left = c - (w * relativeCenterX);
            this._Right = this._Left + w;
            this.CheckSwapX();
            this.OnChangeX();
        }
        /// <summary>
        /// Upraví rozměry v ose Y daným koeficientem (změna výšky) s daným fixním bodem, okolo kterého změna výšky proběhne
        /// </summary>
        /// <param name="coefficientY">Koeficient změny šířky. 1.0f = šířka se nezmění. 0 = šířka se vynuluje. Záporné číslo: objekt se zrcadlí okolo dané souřadnice.</param>
        /// <param name="relativeCenterY">Relativní pozice bodu, okolo které změna proběhne (tento bod se "nepohne"). 0 = Top, 1 = Bottom, 0.5f = střed objektu.</param>
        public void ScaleY(float coefficientY, float relativeCenterY)
        {
            float h = this.Height;
            if (h == 0f || coefficientY == 1f) return;
            float c = this.PointYFromRelative(relativeCenterY);
            h = h * coefficientY;
            this._Top = c - (h * relativeCenterY);
            this._Bottom = this._Top + h;
            this.CheckSwapX();
            this.OnChangeY();
        }
        /// <summary>
        /// Vrátí souřadnici bodu na ose X, který je na dané relativní pozici.
        /// Na pozici 0 je bod Left, na pozici 1 je bod Right, ostatní jsou v odpovídajících pozicích na ose X.
        /// </summary>
        /// <param name="relativeX">Relativní pozice hledaného bodu (0 = Left, 1 = Right)</param>
        /// <returns></returns>
        public float PointXFromRelative(float relativeX)
        {
            float l = this.Width;
            if (l == 0f) return this._Left;
            return this._Left + relativeX * l;
        }
        /// <summary>
        /// Vrátí souřadnici bodu na ose Y, který je na dané relativní pozici.
        /// Na pozici 0 je bod Top, na pozici 1 je bod Bottom, ostatní jsou v odpovídajících pozicích na ose Y.
        /// </summary>
        /// <param name="relativeY">Relativní pozice hledaného bodu (0 = Top, 1 = Bottom)</param>
        /// <returns></returns>
        public float PointYFromRelative(float relativeY)
        {
            float l = this.Height;
            if (l == 0f) return this._Top;
            return this._Top + relativeY * l;
        }
        /// <summary>
        /// Změní velikost prvku přidáním hodnoty ke všem souřadnicím.
        /// Rozměr se typicky změní o dvojnásobek přídavku.
        /// Přídavek může být záporný, pak se rozměry zmenší.
        /// Při zmenšení do záporné velikosti se objekt zmenší na nulovou velikost v daném směru.
        /// </summary>
        /// <param name="addX"></param>
        /// <param name="addY"></param>
        public void Resize(float addX, float addY)
        {
            this.ResizeX(addX);
            this.ResizeY(addY);
        }
        /// <summary>
        /// Změní velikost prvku přidáním hodnoty ke všem souřadnicím.
        /// Rozměr se typicky změní o dvojnásobek přídavku.
        /// Přídavek může být záporný, pak se rozměry zmenší.
        /// Při zmenšení do záporné velikosti se objekt zmenší na nulovou velikost v daném směru.
        /// </summary>
        /// <param name="addX"></param>
        /// <param name="addY"></param>
        public void Resize(float addLeft, float addRight, float addTop, float addBottom)
        {
            this.ResizeX(addLeft, addRight);
            this.ResizeY(addTop, addBottom);
        }
        /// <summary>
        /// Změní velikost prvku ve směru X přidáním hodnoty k souřadnicím Left a Right.
        /// Rozměr Width se typicky změní o dvojnásobek přídavku.
        /// Přídavek může být záporný, pak se rozměry zmenší.
        /// Při zmenšení do záporné velikosti se objekt zmenší na nulovou velikost v daném směru.
        /// </summary>
        /// <param name="addX"></param>
        public void ResizeX(float addX)
        {
            if (addX == 0f) return;
            float w = this.Width / 2f;
            float x = (addX > -w ? addX : -w);
            this._Left -= x;
            this._Right += x;
            this.OnChangeX();
        }
        /// <summary>
        /// Změní velikost prvku ve směru X přidáním hodnoty k souřadnicím Left a Right.
        /// Rozměr se typicky změní o dvojnásobek přídavku.
        /// Přídavek může být záporný, pak se rozměry zmenší.
        /// Při zmenšení do záporné velikosti se objekt zmenší na nulovou velikost v daném směru.
        /// </summary>
        /// <param name="addLeft"></param>
        /// <param name="addRight"></param>
        public void ResizeX(float addLeft, float addRight)
        {
            if (addLeft == 0f && addRight == 0f) return;
            this._Left -= addLeft;
            this._Right += addRight;
            if (this._Left > this._Right)
            {
                float c = this._Right + (this._Left - this._Right) / 2f;
                this._Left -= c;
                this._Right += c;
            }
            this.OnChangeX();
        }
        /// <summary>
        /// Změní velikost prvku ve směru Y přidáním hodnoty k souřadnicím Top a Bottom.
        /// Rozměr Height se typicky změní o dvojnásobek přídavku.
        /// Přídavek může být záporný, pak se rozměry zmenší.
        /// Při zmenšení do záporné velikosti se objekt zmenší na nulovou velikost v daném směru.
        /// </summary>
        /// <param name="addY"></param>
        public void ResizeY(float addY)
        {
            if (addY == 0f) return;
            float h = this.Height / 2f;
            float y = (addY > -h ? addY : -h);
            this._Top -= y;
            this._Bottom += y;
            this.OnChangeY();
        }
        /// <summary>
        /// Změní velikost prvku ve směru Y přidáním hodnoty k souřadnicím Top a Bottom.
        /// Rozměr Height se typicky změní o dvojnásobek přídavku.
        /// Přídavek může být záporný, pak se rozměry zmenší.
        /// Při zmenšení do záporné velikosti se objekt zmenší na nulovou velikost v daném směru.
        /// </summary>
        /// <param name="addTop"></param>
        /// <param name="addBottom"></param>
        public void ResizeY(float addTop, float addBottom)
        {
            if (addTop == 0f && addBottom == 0f) return;
            this._Top -= addTop;
            this._Bottom += addBottom;
            if (this._Top > this._Bottom)
            {
                float c = this._Bottom + (this._Top - this._Bottom) / 2f;
                this._Top -= c;
                this._Bottom += c;
            }
            this.OnChangeY();
        }
        /// <summary>
        /// Změní svoji velikost na danou hodnotu, se zachováním svohu středového bodu.
        /// </summary>
        /// <param name="newSize"></param>
        public void ResizeTo(SizeF newSize)
        {
            this.ResizeTo(newSize.Width, newSize.Height);
        }
        /// <summary>
        /// Změní svoji velikost na danou hodnotu, se zachováním svohu středového bodu.
        /// </summary>
        /// <param name="newSize"></param>
        public void ResizeTo(float width, float height)
        {
            this.Resize((width - this.Width) / 2f, (height - this.Height) / 2f);
        }
        /// <summary>
        /// Přesune se o daný rozměr ve směru X a Y.
        /// Nezmění svoji velikost.
        /// </summary>
        /// <param name="distanceX"></param>
        /// <param name="distanceY"></param>
        public void Move(float distanceX, float distanceY)
        {
            this.MoveX(distanceX);
            this.MoveY(distanceY);
        }
        /// <summary>
        /// Přesune se o daný rozměr ve směru X
        /// Nezmění svoji velikost.
        /// </summary>
        /// <param name="distanceX"></param>
        public void MoveX(float distanceX)
        {
            this._Left += distanceX;
            this._Right += distanceX;
            this.OnChangeX();
        }
        /// <summary>
        /// Přesune se o daný rozměr ve směru Y
        /// Nezmění svoji velikost.
        /// </summary>
        /// <param name="distanceY"></param>
        public void MoveY(float distanceY)
        {
            this._Top += distanceY;
            this._Bottom += distanceY;
            this.OnChangeY();
        }
		/// <summary>
		/// Umístí objekt tak, aby obsadil pravoúhlou oblast mezi dvěma danými body.
		/// </summary>
		/// <param name="point1"></param>
		/// <param name="point2"></param>
		public void SetBound(PointF point1, PointF point2)
		{
			this._Left = point1.X;
			this._Top = point1.Y;
			this._Right = point2.X;
			this._Bottom = point2.Y;
			CheckSwap();
		}
        #endregion
        #region Transformace pomocí Matrixu
		/// <summary>
		/// Provede transformaci objektu this podle daného režimu. 
		/// Střed transformace leží v bodě Center (střed tohoto objektu).
		/// </summary>
		/// <param name="mode"></param>
		public virtual void Transform(MatrixBasicTransformType mode)
        {
            this.Transform(mode, this.PointCenter);
        }
		/// <summary>
		/// Provede transformaci objektu this podle daného režimu. 
		/// Střed transformace leží v daném bodě.
		/// </summary>
		/// <param name="mode"></param>
		/// <param name="center"></param>
        public virtual void Transform(MatrixBasicTransformType mode, PointF center)
        {
            Matrix matrix = Transformation.GetMatrix(mode, center);
            this.Transform(matrix);
        }
		/// <summary>
		/// Provede transformaci objektu this podle daného matrixu. Ten obsahuje vše potřebné.
		/// </summary>
		/// <param name="matrix"></param>
		public virtual void Transform(Matrix matrix)
        {
            PointF[] points = new PointF[] { this.PointTopLeft, this.PointBottomRight } ;
            matrix.TransformPoints(points);
            this.SetBound(points[0], points[1]);
        }
		/// <summary>
		/// Vrátí transformovaný směrník
		/// </summary>
		/// <param name="mode"></param>
		/// <param name="shapeDirection"></param>
		/// <returns></returns>
		public static ShapeDirection TransformDirection(MatrixBasicTransformType mode, ShapeDirection direction)
		{
			switch (mode)
			{
				case MatrixBasicTransformType.RotateLeft:
					switch (direction)
					{
						case ShapeDirection.Left:
							return ShapeDirection.Down;
						case ShapeDirection.Up:
							return ShapeDirection.Left;
						case ShapeDirection.Right:
							return ShapeDirection.Up;
						case ShapeDirection.Down:
							return ShapeDirection.Right;
					}
					break;
				case MatrixBasicTransformType.RotateRight:
					switch (direction)
					{
						case ShapeDirection.Left:
							return ShapeDirection.Up;
						case ShapeDirection.Up:
							return ShapeDirection.Right;
						case ShapeDirection.Right:
							return ShapeDirection.Down;
						case ShapeDirection.Down:
							return ShapeDirection.Left;
					}
					break;
				case MatrixBasicTransformType.Rotate:
					switch (direction)
					{
						case ShapeDirection.Left:
							return ShapeDirection.Right;
						case ShapeDirection.Up:
							return ShapeDirection.Down;
						case ShapeDirection.Right:
							return ShapeDirection.Left;
						case ShapeDirection.Down:
							return ShapeDirection.Up;
					}
					break;
				case MatrixBasicTransformType.MirrorHorizontal:
					switch (direction)
					{
						case ShapeDirection.Left:
							return ShapeDirection.Right;
						case ShapeDirection.Up:
							return ShapeDirection.Up;
						case ShapeDirection.Right:
							return ShapeDirection.Left;
						case ShapeDirection.Down:
							return ShapeDirection.Down;
					}
					break;
				case MatrixBasicTransformType.MirrorVertical:
					switch (direction)
					{
						case ShapeDirection.Left:
							return ShapeDirection.Left;
						case ShapeDirection.Up:
							return ShapeDirection.Down;
						case ShapeDirection.Right:
							return ShapeDirection.Right;
						case ShapeDirection.Down:
							return ShapeDirection.Up;
					}
					break;
			}
			return ShapeDirection.None;
		}
		#endregion
        #region Protected háčky pro potomstvo po změně hodnot Area, aby potomci mohli prověřit svoje navazující hodnoty
        /// <summary>
        /// Tuto metodu volá Area po jakékoli změně hodnot Left, Right, Top, Bottom
        /// </summary>
        protected virtual void OnChange()
        { }
        /// <summary>
        /// Tuto metodu volá Area po jakékoli změně hodnot Left a/nebo Right
        /// </summary>
        protected virtual void OnChangeX()
        { }
        /// <summary>
        /// Tuto metodu volá Area po jakékoli změně hodnot Top a/nebo Bottom
        /// </summary>
        protected virtual void OnChangeY()
        { }
        #endregion
    }
    #region Static podpora pro Matrix
    /// <summary>
    /// Statická třída podporující transformace pomocí matrixu
    /// </summary>
    public static class Transformation
    {
        /// <summary>
        /// Vrátí Matrix pro transformaci požadovaného typu,
        /// a to tak že střed transformace bude uprostřed daného prostoru.
        /// </summary>
        /// <param name="transform">Typ transformace</param>
        /// <param name="area">Prostor, který se má transformovat okolo svého středu</param>
        /// <returns>Matrix, který zajistí transformaci</returns>
        public static Matrix GetMatrix(MatrixBasicTransformType transform, RectangleF area)
        {
            return GetMatrix(transform, new PointF(area.X + area.Width / 2F, area.Y + area.Height / 2F));
        }
        /// <summary>
        /// Vrátí Matrix pro transformaci požadovaného typu,
        /// a to tak že střed transformace bude v daném bodě.
        /// </summary>
        /// <param name="transform">Typ transformace</param>
        /// <param name="center">Bod, okolo kterého se bude tvar transformovat</param>
        /// <returns>Matrix, který zajistí transformaci</returns>
        public static Matrix GetMatrix(MatrixBasicTransformType transform, PointF center)
        {
            switch (transform)
            {
                case MatrixBasicTransformType.RotateLeft:
                    return GetMatrixRotateLeft(center);
                case MatrixBasicTransformType.RotateRight:
                    return GetMatrixRotateRight(center);
                case MatrixBasicTransformType.Rotate:
                    return GetMatrixRotate(center);
                case MatrixBasicTransformType.MirrorHorizontal:
                    return GetMatrixMirrorHorizontal(center);
                case MatrixBasicTransformType.MirrorVertical:
                    return GetMatrixMirrorVertical(center);
            }
            return new Matrix(1, 0, 0, 1, 0, 0);          // void Matrix, sice pracuje, ale dané souřadnice nijak netransformuje. Takové ((1 * 1) == 1)..
        }
        /// <summary>
        /// Vrátí Matrix, který provede rotaci tvaru (Path, Polygon) o 90° VE SMĚRU hodinových ručiček, 
        /// a to tak že střed rotace bude uprostřed daného prostoru.
        /// </summary>
        /// <param name="area">Prostor, který se má otáčet okolo svého středu</param>
        /// <returns>Matrix, který zajistí otáčení</returns>
        public static Matrix GetMatrixRotateRight(RectangleF area)
        {
            return GetMatrixRotateRight(new PointF(area.X + area.Width / 2F, area.Y + area.Height / 2F));
        }
        /// <summary>
        /// Vrátí Matrix, který provede rotaci tvaru (Path, Polygon) o 90° VE SMĚRU hodinových ručiček, 
        /// a to tak že střed rotace bude v daném bodě.
        /// </summary>
        /// <param name="center">Bod, okolo kterého se bude tvar otáčet</param>
        /// <returns>Matrix, který zajistí otáčení</returns>
        public static Matrix GetMatrixRotateRight(PointF center)
        {
			float dx = center.X - center.Y;
			float dy = center.Y - center.X;
			return new Matrix(0F, 1F, 1F, 0F, dx, dy);
        }
        /// <summary>
        /// Vrátí Matrix, který provede rotaci tvaru (Path, Polygon) o 90° PROTI SMĚRU hodinových ručiček, 
        /// a to tak že střed rotace bude uprostřed daného prostoru.
        /// </summary>
        /// <param name="area">Prostor, který se má otáčet okolo svého středu</param>
        /// <returns>Matrix, který zajistí otáčení</returns>
        public static Matrix GetMatrixRotateLeft(RectangleF area)
        {
            return GetMatrixRotateLeft(new PointF(area.X + area.Width / 2F, area.Y + area.Height / 2F));
        }
        /// <summary>
        /// Vrátí Matrix, který provede rotaci tvaru (Path, Polygon) o 90° PROTI SMĚRU hodinových ručiček, 
        /// a to tak že střed rotace bude v daném bodě.
        /// </summary>
        /// <param name="center">Bod, okolo kterého se bude tvar otáčet</param>
        /// <returns>Matrix, který zajistí otáčení</returns>
        public static Matrix GetMatrixRotateLeft(PointF center)
        {
			float dx = center.X + center.Y;
			float dy = center.Y - center.X;
			return new Matrix(0F, 1F, -1F, 0F, dx, dy);
        }
        /// <summary>
        /// Vrátí Matrix, který provede zrcadlení tvaru (Path, Polygon) vodorovně = zleva doprava, a zprava doleva,
        /// a to tak že střed zrcadlení bude uprostřed daného prostoru.
        /// </summary>
        /// <param name="area">Prostor, který se má zrcadlit okolo svého středu</param>
        /// <returns>Matrix, který zajistí zrcadlení</returns>
        public static Matrix GetMatrixMirrorHorizontal(RectangleF area)
        {
            return GetMatrixMirrorHorizontal(new PointF(area.X + area.Width / 2F, area.Y + area.Height / 2F));
        }
        /// <summary>
        /// Vrátí Matrix, který provede zrcadlení tvaru (Path, Polygon) vodorovně = zleva doprava, a zprava doleva,
        /// a to tak že střed zrcadlení bude v daném bodě.
        /// </summary>
        /// <param name="center">Bod, okolo kterého se bude tvar zrcadlit</param>
        /// <returns>Matrix, který zajistí zrcadlení</returns>
        public static Matrix GetMatrixMirrorHorizontal(PointF center)
        {
            float dx = center.X + center.X;
            float dy = 0F;
            return new Matrix(-1F, 0F, 0F, 1F, dx, dy);
        }
        /// <summary>
        /// Vrátí Matrix, který provede zrcadlení tvaru (Path, Polygon) svisle = shora dolů, a sdola nahoru,
        /// a to tak že střed zrcadlení bude uprostřed daného prostoru.
        /// </summary>
        /// <param name="area">Prostor, který se má zrcadlit okolo svého středu</param>
        /// <returns>Matrix, který zajistí zrcadlení</returns>
        public static Matrix GetMatrixMirrorVertical(RectangleF area)
        {
            return GetMatrixMirrorVertical(new PointF(area.X + area.Width / 2F, area.Y + area.Height / 2F));
        }
        /// <summary>
        /// Vrátí Matrix, který provede zrcadlení tvaru (Path, Polygon) svisle = shora dolů, a sdola nahoru,
        /// a to tak že střed zrcadlení bude v daném bodě.
        /// </summary>
        /// <param name="center">Bod, okolo kterého se bude tvar zrcadlit</param>
        /// <returns>Matrix, který zajistí zrcadlení</returns>
        public static Matrix GetMatrixMirrorVertical(PointF center)
        {
            float dx = 0F;
            float dy = center.Y + center.Y;
            return new Matrix(1F, 0F, 0F, -1F, dx, dy);
        }
        /// <summary>
        /// Vrátí Matrix, který provede rotaci tvaru (Path, Polygon) o 180°, což je stejné jako zrcadlení horizontální i vertikální, 
        /// a to tak že střed otáčení = zrcadlení bude uprostřed daného prostoru.
        /// </summary>
        /// <param name="area">Prostor, který se má otáčet okolo svého středu</param>
        /// <returns>Matrix, který zajistí zrcadlení</returns>
        public static Matrix GetMatrixRotate(RectangleF area)
        {
            return GetMatrixRotate(new PointF(area.X + area.Width / 2F, area.Y + area.Height / 2F));
        }
        /// <summary>
        /// Vrátí Matrix, který provede rotaci tvaru (Path, Polygon) o 180°, což je stejné jako zrcadlení horizontální i vertikální, 
        /// a to tak že střed otáčení = zrcadlení bude v daném bodě.
        /// </summary>
        /// <param name="center">Bod, okolo kterého se bude tvar otáčet</param>
        /// <returns>Matrix, který zajistí zrcadlení</returns>
        public static Matrix GetMatrixRotate(PointF center)
        {
            float dx = center.X + center.X;
            float dy = center.Y + center.Y;
            return new Matrix(-1F, 0F, 0F, -1F, dx, dy);
        }
    }
    /// <summary>
    /// Druhy transformací, pro které lze vygenerovat matrix v metodě GetMatrix(MatrixBasicTransformType).
    /// Udává jeden z několika základních druhů transformací.
    /// </summary>
    public enum MatrixBasicTransformType
    {
        /// <summary>Žádná transformace</summary>
        NoTransform = 1,
        /// <summary>Otočení doprava o 90° = ve směru hodinových ručiček</summary>
        RotateRight,
        /// <summary>Otočení doleva o 90° = proti směru hodinových ručiček</summary>
        RotateLeft,
        /// <summary>Otočení o 180°, stejné jako dvojité zrcadlení</summary>
        Rotate,
        /// <summary>Zrcadlení horizontální = zprava doleva a zleva doprava</summary>
        MirrorHorizontal,
        /// <summary>Zrcadlení vertikální = shora dolů, a sdola nahoru</summary>
        MirrorVertical
    }        
    #endregion
}
